home *** CD-ROM | disk | FTP | other *** search
/ Directorty Opus 5 - Magellan 2 / Opus 5 - Magellan 2.iso / Extras / compare_source / source / compare.module.c < prev    next >
C/C++ Source or Header  |  1996-10-14  |  42KB  |  1,157 lines

  1. /*######################################################################################
  2. ## compare.module by Leo 'Nudel' Davidson for Gods'Gift Utilities.                      ##
  3. ## A plug-in module for Directory Opus 5.5 to compare the contents of two files.      ##
  4. ##                                                                                      ##
  5. ##                                                                                      ##
  6. ## Until July 1998 you should be able to contact me via any of the following:          ##
  7. ##                                                                                      ##
  8. ## email: leo.davidson@keble.oxford.ac.uk                                              ##
  9. ##   www: http://users.ox.ac.uk/~kebl0364                                              ##
  10. ##   IRC: Nudel in #Amiga on Effnet or Undernet (very rarely).                          ##
  11. ##                                                                                      ##
  12. ## Comments, suggestions, questions, offers, and chats all welcome.                      ##
  13. ##                                                                                      ##
  14. ##                                                                                      ##
  15. ## Tabsize: 4 -- 88 Columns (sorry) -- Amiga-specific -- Compile with SAS/C.          ##
  16. ##                                                                                      ##
  17. ## Credit is due to Nick Christie, Jonathan Potter, and Greg Perry for their advice,  ##
  18. ## examples, and general help beyond the call of duty. Thanks guys!                      ##
  19. ##************************************************************************************##
  20. ## There appears to be a bug in DOpus 5.5: Each call to AsyncRequestTags() looses      ##
  21. ## 32 bytes of memory. This also happens in the example module which comes with the      ##
  22. ## OpusSDK and has been reported to GPSoftware.                                          ##
  23. ########################################################################################
  24. ## This program rarely allocates more than about 30k at a time. Since all error          ##
  25. ## messages are output by requesters (which take quite a bit of mem to display),      ##
  26. ## memory allocation failures result in a silent abort. Perhaps it would be better      ##
  27. ## to at least call DisplayBeep() -- maybe in the future.                              ##
  28. ## In the places where large allocations are possible (while generating the full      ##
  29. ## report, mostly), error requesters ARE implimented.                                  ##
  30. ########################################################################################
  31. ## I almost added the option to specify the files to compare on the command-line, but ##
  32. ## I can't see any point or use for it.                                                  ##
  33. ######################################################################################*/
  34.  
  35. #include "compare.module.h"
  36.  
  37. /**************************************************************************************/
  38.  
  39. #define PROGNAME "compare.module"
  40. #define PROGVERS "1.2"
  41. #define PROGDATE __AMIGADATE__
  42. static char version_str[] = "\0$VER: " PROGNAME " " PROGVERS " " PROGDATE "\0";
  43. // Above line should be double null-terminated.
  44.  
  45. /*= Definition of the module =========================================================*/
  46. ModuleInfo module_info =
  47. {
  48.         1,                                    // Version
  49.         "compare.module",                    // Module name
  50.         "compare.catalog",                    // Catalog name
  51.         0,                                    // Flags
  52.         1,                                    // Number of functions
  53.         {0,"Compare",MSG_COMPARE_DESC,\
  54.          FUNCF_NEED_SOURCE|FUNCF_NEED_FILES|FUNCF_SINGLE_SOURCE,\
  55.          0}                                    // First function.
  56. };
  57.  
  58. /**************************************************************************************/
  59.  
  60. /*= L_Module_Entry() =================================================================-.
  61. || Main entry point to the module. The L_ is to identify this as a library              ||
  62. || function (specified by the "libprefix" option in the makefile)                      ||
  63. ||------------------------------------------------------------------------------------||
  64. || This kinda evolved into one huge routine and at this stage isn't really worth      ||
  65. || cutting down. I'd definately split things up if I did it all again, though.          ||
  66. `-====================================================================================*/
  67. int __asm __saveds L_Module_Entry(
  68.     register __a0 char *args,                // User-supplied arguments
  69.     register __a1 struct Screen *screen,    // Screen to open on
  70.     register __a2 IPCData *ipc,                // Our IPC pointer
  71.     register __a3 IPCData *main_ipc,        // Main Opus IPC pointer
  72.     register __d0 ULONG mod_id,                // ID of module function
  73.     register __d1 EXT_FUNC(func_callback))    // Opus callback function
  74. {
  75.     Compare_Data *data;                // Pseudo-global variables.
  76.     FuncArgs *fa;
  77.     LONG diffbytes;                    // Number of differences between the files.
  78.  
  79.     if (data = AllocVec(sizeof(Compare_Data),MEMF_CLEAR))
  80.     {
  81.         data->ipc = ipc;
  82.         data->func_callback = func_callback;
  83.  
  84.         if (data->rnd.poolhead = \
  85.             NewMemHandle(PUDDLESIZE,THRESHSIZE,MEMF_CLEAR) )
  86.         {
  87.             data->rnd.data = data;
  88.             if (DOpusBase->lib_Version < MIN_OPUS_VERSION)
  89.                 informUser(data,dgs(MSG_VERSREQ_FMT),FALSE,NULL,MIN_OPUS_VERSION);
  90.             else
  91.             {
  92.                 fa = parseArgs(data,args);    // Parse command-line arguments.
  93.  
  94.                 if ((get_files_to_compare(data,data->file1path,data->file1name,
  95.                                                data->file2path,data->file2name)) \
  96.                     && (data->file1rn = createResNode(&data->rnd)) \
  97.                     && (data->file2rn = createResNode(&data->rnd)) \
  98.                     && (open_files_to_compare(data)) \
  99.                     && ( (diffbytes = compare_files(data)) >= 0 ) )
  100.                 {
  101.                     // Note: The files being compared remain open for later.
  102.                     //       (They'll be closed by ResNodes automatically on abort.)
  103.  
  104.                     // Report the number of differences, and, if there were any, ask
  105.                     // if the user wants a full display of them.
  106.                     if (report_num_diffs(data,diffbytes))
  107.                     {
  108.                         // If they asked for a full display, generate one.
  109.                         report_full_display(data);
  110.                     }
  111.                     // If more is done before the call to deleteAllResNodes()
  112.                     // just below, file1rn and file2rn should be deleted unless
  113.                     // the files are still wanted (remember they are still open).
  114.                 }
  115.                 freeArgs(fa);            // Free FuncArgs structure, if any.
  116.             }
  117.             // Free all remainding resources allocated with ResNodes.
  118.             deleteAllResNodes(&data->rnd);    // Safe to call even if no ResNodes.
  119.  
  120.             // Free our memory pool.
  121.             FreeMemHandle(data->rnd.poolhead);
  122.             data->rnd.poolhead = NULL;
  123.         }
  124.         // Free our pseudo-global variables.
  125.         FreeVec(data);
  126.     }
  127.     // Currently, functions should always return 1.
  128.     return(1);
  129. }
  130.  
  131. /*= InformUser() =====================================================================-.
  132. || Send the user a requester with printf-style formatted text.                          ||
  133. || Requester is centered on the Opus screen and has just an "OK" gadget.              ||
  134. || If window is TRUE it'll try to open over the lister window.                              ||
  135. ||------------------------------------------------------------------------------------||
  136. || Flags: IU_DISPLAY -- changes gadgets to "Display" and "OK" and returns 1 or 0 resp.||
  137. `-====================================================================================*/
  138. BOOL informUser(Compare_Data *data,char *format,BOOL window,ULONG flags,...)
  139. {
  140.     BOOL iu_return;
  141.     struct Window *win;
  142.     struct Screen *screen;
  143.     ResNode *rn1;
  144.     va_list  ap;
  145.  
  146.     if (rn1 = allocNewResNode(&data->rnd,INFORMUSERBUFFERSIZE))
  147.     {
  148.         // Build requester text
  149.         va_start(ap,flags);
  150.         vsprintf(rn1->rn_Mem,format,ap);
  151.         va_end(ap);
  152.  
  153.         if (window)
  154.         {
  155.             win = getListerWindow(data);
  156.             screen = NULL;
  157.         }
  158.         else
  159.         {
  160.             screen = getDOpusScreen(data);
  161.             win = NULL;
  162.         }
  163.  
  164.         // Display requester over window.
  165.         iu_return = AsyncRequestTags(data->ipc, REQTYPE_SIMPLE, 0, 0, 0,
  166.             TAGIF(win,AR_Window),    win,
  167.             TAGIF((!win) && screen,AR_Screen),    screen,
  168.             AR_Message,                rn1->rn_Mem,
  169.             AR_Title,                dgs(MSG_TITLE),
  170.             TAGIF(flags & IU_DISPLAY,AR_Button),    dgs(MSG_DISPLAY_GAD),
  171.             TAGIF(flags & IU_DISPLAY,AR_Button),    dgs(MSG_NODISPLAY_GAD),
  172.             TAGNOT(flags & IU_DISPLAY,AR_Button),    dgs(MSG_OK_GAD),
  173.             TAG_END);
  174.  
  175.         // Free memory allocated for buffer.
  176.         deleteResNode(&data->rnd,rn1);
  177.     }
  178.     return(iu_return);
  179. }
  180.  
  181. /*= Get_Files_To_Compare() ===========================================================-.
  182. || Fills in the file paths and names of the two files to be compared.                  ||
  183. || If two files are selected in the Source lister they will be chosen. Otherwise,      ||
  184. || the file selected in the Souce lister is chosen along with the first one in the      ||
  185. || Destination lister.                                                                  ||
  186. || Returns boolean success.                                                              ||
  187. || Does *not* report any errors (e.g. not enough selected files) to the user to be      ||
  188. || be in keeping with how the other Opus commands behave.                              ||
  189. `-====================================================================================*/
  190. BOOL get_files_to_compare(Compare_Data *data,char *path1,char *name1,\
  191.                                              char *path2,char *name2)
  192. {
  193.     struct path_node *pn1;
  194.     ResNode *rn1;
  195.     LONG src_entries;
  196.     BOOL gftc_return = FALSE;
  197.  
  198.     // Allocate a temporary path buffer.
  199.     if (rn1 = allocNewResNode(&data->rnd,PATHBUFFSIZE))
  200.     {
  201.         // path_node to pn1 and path string to ResNode's allocated buffer.
  202.         // If no source lister is returned silently abort. There is not much
  203.         // point in an error message because Opus won't let this function run
  204.         // without a source lister, so something really bad has happened.
  205.         if (pn1 = (struct path_node *)\
  206.             data->func_callback(EXTCMD_GET_SOURCE,IPCDATA(data->ipc),rn1->rn_Mem) )
  207.         {
  208.             // Store the handle of our lister for later use.
  209.             (data->listerhandle) = (pn1->lister);
  210.  
  211.             src_entries =\
  212.                 data->func_callback(EXTCMD_ENTRY_COUNT,IPCDATA(data->ipc),NULL);
  213.  
  214.             switch(src_entries)
  215.             {
  216.                 // If there are no files selected something really bad has
  217.                 // happened as Opus shouldn't have started this function at all.
  218.                 case 0:    break;
  219.  
  220.                 // If there's only one file selected we have to find the other
  221.                 // in the Destination lister.
  222.                 case 1:    gftc_return = get_s_and_d(data,rn1,path1,name1,path2,name2);
  223.                         break;
  224.  
  225.                 // If there's 2 or more selected, use the first two.
  226.                 default: gftc_return = get_s_twice(data,rn1,path1,name1,path2,name2);
  227.                          break;
  228.             }
  229.         }
  230.         // Free memory allocated for buffer.
  231.         deleteResNode(&data->rnd,rn1);
  232.     }
  233.     return(gftc_return);
  234. }
  235.  
  236. /*= get_s_twice() ====================================================================-.
  237. || Used by Get_Files_To_Compare() to get two file from the source lister.              ||
  238. || Returns boolean success.                                                              ||
  239. `-====================================================================================*/
  240. BOOL get_s_twice(Compare_Data *data,ResNode *rn1,char *path1,char *name1,
  241.                                                  char *path2,char *name2)
  242. {
  243.     BOOL gst_return = FALSE;
  244.     struct function_entry *fentry;
  245.     struct endentry_packet eep;
  246.  
  247.     // Put the source lister path at the front of both path strings.
  248.     strcpy(path1,rn1->rn_Mem);
  249.     strcpy(path2,rn1->rn_Mem);
  250.  
  251.     // Get first entry from source lister.
  252.     if (fentry = (struct function_entry *)\
  253.      data->func_callback(EXTCMD_GET_ENTRY,IPCDATA(data->ipc),NULL))
  254.     {
  255.         strcat(path1,fentry->name);
  256.         strcpy(name1,fentry->name);
  257.  
  258.         // Finish with the entry and deselect it.
  259.         eep.entry = fentry;
  260.         eep.deselect = TRUE;
  261.         data->func_callback(EXTCMD_END_ENTRY,IPCDATA(data->ipc),&eep);
  262.  
  263.         // Now get the second entry and do the same thing with it.
  264.         if (fentry = (struct function_entry *)\
  265.          data->func_callback(EXTCMD_GET_ENTRY,IPCDATA(data->ipc),NULL))
  266.         {
  267.             strcat(path2,fentry->name);
  268.             strcpy(name2,fentry->name);
  269.  
  270.             // Finish with the entry and deselect it.
  271.             eep.entry = fentry;
  272.             eep.deselect = TRUE;
  273.             data->func_callback(EXTCMD_END_ENTRY,IPCDATA(data->ipc),&eep);
  274.  
  275.             // We got our filenames/paths okay.
  276.             gst_return = TRUE;
  277.         }
  278.     }
  279.     return(gst_return);
  280. }
  281.  
  282. /*= get_s_and_d() ====================================================================-.
  283. || Used by Get_Files_To_Compare() to get one file from the source and one from the      ||
  284. || destination lister. Returns boolean success.                                          ||
  285. `-====================================================================================*/
  286. BOOL get_s_and_d(Compare_Data *data,ResNode *rn1,char *path1,char *name1,
  287.                                                  char *path2,char *name2)
  288. {
  289.     BOOL gsad_return = FALSE;
  290.     struct function_entry *fentry;
  291.     struct endentry_packet eep;
  292.     struct command_packet cp;
  293.     char destlister[24];
  294.     ResNode *combufrn;
  295.  
  296.     // Allocate a temporary buffer to build ARexx commands in.
  297.     if (combufrn = allocNewResNode(&data->rnd,COMMBUFFSIZE))
  298.     {
  299.         // Put the source lister path at the front of the first path string.
  300.         strcpy(path1,rn1->rn_Mem);
  301.  
  302.         // Get entry from source lister.
  303.         if (fentry = (struct function_entry *)\
  304.                         data->func_callback(EXTCMD_GET_ENTRY,IPCDATA(data->ipc),NULL))
  305.         {
  306.             strcat(path1,fentry->name);
  307.             strcpy(name1,fentry->name);
  308.  
  309.             // Finish with the entry and deselect it.
  310.             eep.entry = fentry;
  311.             eep.deselect = TRUE;
  312.             data->func_callback(EXTCMD_END_ENTRY,IPCDATA(data->ipc),&eep);
  313.  
  314. /* There are not callback functions to deal with entries in the dest lister, so
  315.    we have to do it with ARexx. -- There are callback functions to GET the
  316.    dest lister but if we used them it would not be safe to unbusy it before
  317.    the operation is completed. */
  318.  
  319.             // First, get the dest lister's handle.
  320.             // If we can't get a dest, silently fail to be consistent with what
  321.             // Opus does internally.
  322.  
  323.             strcpy(combufrn->rn_Mem,"lister query dest");
  324.             cp.command = combufrn->rn_Mem;
  325.             cp.flags = COMMANDF_RESULT;
  326.             if ( (data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),&cp)) && \
  327.                  (cp.result) && (cp.result[0]) )
  328.             {
  329.                 copyword(destlister,cp.result);
  330.                 FreeVec(cp.result);
  331.  
  332.                 // As fast as possible, make the lister busy.
  333.                 sprintf(combufrn->rn_Mem,"lister set %s busy %s wait",
  334.                         destlister,"on");
  335.                 sendExtCmd_nr(data,combufrn->rn_Mem,&cp);
  336.  
  337.                 // Get the lister's path and put it at start of second filepath.
  338.                 sprintf(combufrn->rn_Mem,"lister query %s path",destlister);
  339.                 cp.command = combufrn->rn_Mem;
  340.                 cp.flags = COMMANDF_RESULT;
  341.                 if ( (data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),&cp))\
  342.                     && (cp.result) && (cp.result[0]) )
  343.                 {
  344.                     strcpy(path2,cp.result);
  345.                     FreeVec(cp.result);
  346.  
  347.                     // Get the name of the first selected file, if any.
  348.                     sprintf(combufrn->rn_Mem,"lister query %s selfiles",destlister);
  349.                     cp.command = combufrn->rn_Mem;
  350.                     cp.flags = COMMANDF_RESULT;
  351.                     if ( (data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),
  352.                             &cp)) && (cp.result) && (cp.result[0]) )
  353.                     {
  354.                         copywordquoted(name2,cp.result);
  355.                         strcat(path2,name2);
  356.  
  357.                         // Now we just have to deselect the entry.
  358.                         // Filename already has quotes around it.
  359.                         sprintf(combufrn->rn_Mem,"lister select %s %s 0",
  360.                                 destlister,cp.result);
  361.  
  362.                         FreeVec(cp.result);
  363.  
  364.                         sendExtCmd_nr(data,combufrn->rn_Mem,&cp);
  365.  
  366.                         sprintf(combufrn->rn_Mem,"lister refresh %s",destlister);
  367.                         sendExtCmd_nr(data,combufrn->rn_Mem,&cp);
  368.  
  369.                         // We got our filenames/paths okay.
  370.                         gsad_return = TRUE;
  371.                     }
  372.                     else if (cp.result)
  373.                     {
  374.                         FreeVec(cp.result);
  375.                     }
  376.                 }
  377.                 else if (cp.result)
  378.                 {
  379.                     FreeVec(cp.result);
  380.                 }
  381.  
  382.                 // Free-up the destination lister.
  383.                 sprintf(combufrn->rn_Mem,"lister set %s busy %s wait",destlister,"off");
  384.                 sendExtCmd_nr(data,combufrn->rn_Mem,&cp);
  385.             }
  386.             else if (cp.result)
  387.             {
  388.                 FreeVec(cp.result);
  389.             }
  390.         }
  391.         // Free memory allocated for command buffer.
  392.         deleteResNode(&data->rnd,combufrn);
  393.     }
  394.     return(gsad_return);
  395. }
  396.  
  397. /*= open_files_to_compare() ==========================================================-.
  398. || Attempts to open the two files in preparation for comparison.                      ||
  399. || Returns boolean success and will fail if the files cannot be openned, or cannot be ||
  400. || be examined.                                                                          ||
  401. `-====================================================================================*/
  402. BOOL open_files_to_compare(Compare_Data *data)
  403. {
  404.     BOOL oftc_return = FALSE;
  405.  
  406.     // Give the ResNodes filenames.
  407.     (data->file1rn->rn_Name) = (data->file1path);
  408.     (data->file2rn->rn_Name) = (data->file2path);
  409.  
  410.     // Attempt to get the size of each file.
  411.     if ( !( (examineResNode(&data->rnd,data->file1rn)) && \
  412.             (examineResNode(&data->rnd,data->file2rn)) ) )
  413.     {
  414.         // Error message and abort if we couldn't get either size.
  415.         informUser(data,dgs(MSG_EXAMFAIL),TRUE,NULL);
  416.     }
  417.     else
  418.     {
  419.         if ((data->file1rn->rn_FIB->fib_Size) != (data->file2rn->rn_FIB->fib_Size))
  420.         {
  421.             (data->minsize) = min((data->file1rn->rn_FIB->fib_Size),
  422.                                   (data->file2rn->rn_FIB->fib_Size));
  423.  
  424.             // Warn them that the files are of different sizes and only the first
  425.             // xxx bytes will be compared.
  426.             if (!(data->nosizewarn))
  427.                 informUser(data,dgs(MSG_DIFFSIZES_FMT),TRUE,NULL,data->minsize);
  428.         }
  429.         else
  430.             (data->minsize) = (data->file1rn->rn_FIB->fib_Size);
  431.  
  432.         if (!( (openFileResNode(&data->rnd,data->file1rn,MODE_OLDFILE))
  433.             && (openFileResNode(&data->rnd,data->file2rn,MODE_OLDFILE)) ))
  434.         {
  435.             // Error message and abort if we couldn't actually open either file.
  436.             informUser(data,dgs(MSG_OPENFAIL),TRUE,NULL);
  437.         }
  438.         else
  439.             oftc_return = TRUE;
  440.     }
  441.     return(oftc_return);
  442. }
  443.  
  444. /*= compare_files() ==================================================================-.
  445. || Actually compares the files which have been made ready by previous routines.          ||
  446. || Builds a linked list of DiffRange's which describe where the differences are.      ||
  447. || Returns the number of different bytes, or (-1) on failure/user-abort.              ||
  448. `-====================================================================================*/
  449. LONG compare_files(Compare_Data *data)
  450. {
  451.     LONG cf_return = -1;    // We return the number of different bytes, or -1 on error.
  452.     APTR progwin;    // Handle of progress window.
  453.     LONG remfsize;    // Bytes remaining to be compared.
  454.     LONG donebytes;    // Total bytes done.
  455.     LONG diffbytes;    // Total bytes different.
  456.     LONG dobytes;    // Bytes remaining in current loop.
  457.     BOOL comperror;    // If true there has been an error while comparing.
  458.     LONG infotxtid;    // Used for choosing one out of a set of locale strings.
  459.     LONG lastdiff;    // Offset from start of file to the last different byte.
  460.     LONG thisdiff;    // Offset from start of file to the current different byte.
  461.     LONG thisdiffstart;    // -._ Form an interval around the current
  462.     LONG thisdiffend;    // -'  different byte (offsets from start of file).
  463.     struct DiffRange *drlast;    // Last dr in linked list. (Base is data->drbase)
  464.     struct DiffRange *drnew;    // New/current DiffRange structure.
  465.     char *cp1;        // Pointer to current posn. in compare buffer1.
  466.     char *cp2;        // Pointer to current posn. in compare buffer2.
  467.     char *cpe;        // Pointer to end of compare buffer1.
  468.     char *cpo;        // Pointer to start of compare buffer1.
  469.     ResNode *progbrn;    // ResNode for memory allocated for building progress msg.
  470.  
  471.     remfsize = (data->minsize);
  472.  
  473.     // Open progress window.
  474.     if (progwin = OpenProgressWindowTags(\
  475.                 PW_Window,        getListerWindow(data),
  476.                 PW_Title,        dgs(MSG_PROGTITLE),
  477.                 PW_FileName,    data->file1name,
  478.                 PW_FileCount,    remfsize,
  479.                 PW_Flags,        PWF_INFO|PWF_GRAPH|PWF_ABORT|PWF_FILENAME,
  480.                 TAG_END))
  481.     {
  482.         // Allocate comparison buffers. Error message and abort if we can't.
  483.         if (!(allocMemResNode(&data->rnd,data->file1rn,data->buffsize) && \
  484.               allocMemResNode(&data->rnd,data->file2rn,data->buffsize) && \
  485.               (progbrn = allocNewResNode(&data->rnd,NAMEBUFFSIZE+50)) ))
  486.         {
  487.             informUser(data,dgs(MSG_CMPBUFFAIL),TRUE,NULL);
  488.         }
  489.         else
  490.         {
  491.             // Initialize some variables (remfsize initialized above).
  492.             (data->drbase) = drlast = NULL;
  493.             // Make sure first difference starts a new new DiffRange
  494.             lastdiff = (-(4*DR_EXTEND));
  495.             donebytes = diffbytes = 0;
  496.             comperror = FALSE;
  497.  
  498.             while( (remfsize > 0) && (!(CheckProgressAbort(progwin))) && \
  499.                  (!(data->func_callback(EXTCMD_CHECK_ABORT,IPCDATA(data->ipc),NULL))) \
  500.                  && (!comperror) )
  501.             {
  502.                 // Use a different string depending on how many bytes are different.
  503.                 // No crappy "xxx error(s)" messages -- using the correct
  504.                 // singular/plural form is quick'n'easy and not doing so is just lazy.
  505.                 switch (diffbytes)
  506.                 {
  507.                     case 0:        infotxtid = MSG_DIFFPROG0_FMT; break;
  508.                     case 1:        infotxtid = MSG_DIFFPROG1_FMT; break;
  509.                     default:    infotxtid = MSG_DIFFPROGP_FMT; break;
  510.                 }
  511.                 sprintf(progbrn->rn_Mem,dgs(infotxtid),data->file2name,diffbytes);
  512.                 SetProgressWindowTags(progwin,
  513.                     PW_FileNum,donebytes,
  514.                     PW_Info,progbrn->rn_Mem,
  515.                     TAG_END);
  516.  
  517.                 // If we filled the compare buffers, compare the entire buffers,
  518.                 // otherwise compare until the end of the file.
  519.                 dobytes = min(remfsize,(data->buffsize));
  520.  
  521.                 // Update the number of bytes remaining to be compared after this
  522.                 // block is done. remfsize will go <= 0 when we're done.
  523.                 remfsize -= (data->buffsize);
  524.  
  525.                 // Read the data for the next comparison from the two files.
  526.                 if ((0>=Read(data->file1rn->rn_FHandle,data->file1rn->rn_Mem,dobytes))\
  527.                  || (0>=Read(data->file2rn->rn_FHandle,data->file2rn->rn_Mem,dobytes)))
  528.                 {
  529.                     // If we couldn't read for some reason, report the error
  530.                     // and flag that we should abort.
  531.                     comperror = TRUE;
  532.                     informUser(data,dgs(MSG_READFAIL),TRUE,NULL);
  533.                 }
  534.  
  535.                 // Setup the buffer pointers.
  536.                 cp1 = (data->file1rn->rn_Mem);
  537.                 cp2 = (data->file2rn->rn_Mem);
  538.                 cpo = cp1;
  539.                 cpe = cp1 + dobytes;
  540.  
  541.                 // While there isn't an error, compare the two buffers, storing
  542.                 // the ranges in which differences occur.
  543.  
  544.                 for (; (!comperror) && (cp1 < cpe); cp1++,cp2++)
  545.                 {
  546.                     if ((*cp1) != (*cp2))
  547.                     {
  548.                         diffbytes++;
  549.                         thisdiff = (cp1 - cpo) + donebytes;
  550.  
  551.                         // We don't have to worry about pointing outside the
  552.                         // file here because the routine which deals with the
  553.                         // DiffRanges handles that.
  554.                         thisdiffstart = thisdiff - (thisdiff%8);
  555.                         thisdiffend = thisdiff + ((8 - (thisdiff%8)) - 1);
  556.  
  557.                         // If this difference is within 3 * DR_EXTEND bytes of
  558.                         // the previous one, just extend the previous difference range.
  559.                         // Otherwise, create a new difference range of just this byte.
  560.  
  561.                         if ((thisdiffend - lastdiff) <= (3*DR_EXTEND))
  562.                             (drlast->end) = thisdiffend;
  563.                         else
  564.                         {
  565.                             // We don't bother tracking the allocations of the
  566.                             // Difference Ranges: they'll be freed when the pool is
  567.                             // destroyed and don't need to be freed before that.
  568.                             if (!(drnew = AllocMemH(data->rnd.poolhead,
  569.                                             sizeof(struct DiffRange)) ))
  570.                             {
  571.                                 comperror = TRUE;
  572.                                 informUser(data,dgs(MSG_OUTOFMEM),TRUE,NULL);
  573.                                 DisplayBeep(NULL);
  574.                             }
  575.                             else
  576.                             {
  577.                                 if (!(data->drbase))
  578.                                     (data->drbase) = drlast = drnew;
  579.                                 else
  580.                                 {
  581.                                     (drlast->next) = drnew;
  582.                                     drlast = drnew;
  583.                                 }
  584.                                 (drnew->start) = thisdiffstart;
  585.                                 (drnew->end) = thisdiffend;
  586.                             }
  587.                         }
  588.                         lastdiff = (thisdiffend + 1);
  589.                     }
  590.                 }
  591.                 donebytes += dobytes;
  592.             }
  593.  
  594.             // Only signal that the rest of the program should continue if there
  595.             // wasn't an error and the user didn't abort before the end of the files.
  596.             if ((!comperror) && (remfsize <= 0))
  597.             {
  598.                 cf_return = diffbytes;
  599.  
  600.                 // Make sure the progress window reaches 100% done.
  601.                 switch (diffbytes)
  602.                 {
  603.                     case 0:        infotxtid = MSG_DIFFPROG0_FMT; break;
  604.                     case 1:        infotxtid = MSG_DIFFPROG1_FMT; break;
  605.                     default:    infotxtid = MSG_DIFFPROGP_FMT; break;
  606.                 }
  607.                 sprintf(progbrn->rn_Mem,dgs(infotxtid),data->file2name,diffbytes);
  608.                 SetProgressWindowTags(progwin,
  609.                     PW_FileNum,donebytes,
  610.                     PW_Info,progbrn->rn_Mem,
  611.                     TAG_END);
  612.             }
  613.  
  614.             // Free memory for the comparison buffers.
  615.             deleteResNode(&data->rnd,progbrn);
  616.             freeMemResNode(&data->rnd,data->file1rn);
  617.             freeMemResNode(&data->rnd,data->file2rn);
  618.         }
  619.         CloseProgressWindow(progwin);
  620.     }
  621.     return(cf_return);
  622. }
  623.  
  624. /*= report_num_diffs() ===============================================================-.
  625. || Reports the number of differences to the user and gives them the option of a full  ||
  626. || display of them. Returns TRUE if they do, FALSE if they don't. (Note that they      ||
  627. || cannot ask for a full display when the files are identical.)                          ||
  628. `-====================================================================================*/
  629. BOOL report_num_diffs(Compare_Data *data,LONG diffbytes)
  630. {
  631.     BOOL rnd_return = FALSE;
  632.  
  633.     switch (diffbytes)
  634.     {
  635.         case 0:
  636.             rnd_return = FALSE;
  637.             informUser(data,dgs(MSG_NUMDIFFS0),TRUE,NULL);
  638.             break;
  639.         case 1:
  640.             rnd_return = \
  641.                     informUser(data,dgs(MSG_NUMDIFFS1_FMT),TRUE,IU_DISPLAY,diffbytes);
  642.             break;
  643.         default:
  644.             rnd_return = \
  645.                 informUser(data,dgs(MSG_NUMDIFFSP_FMT),TRUE,IU_DISPLAY,diffbytes);
  646.             break;
  647.     }
  648.     return(rnd_return);
  649. }
  650.  
  651. /*= getListerWindow() ================================================================-.
  652. || Gets the window of a lister from just the lister handle (in ASCII).                  ||
  653. || Returns the handle, or NULL. This should not be stored for later use as the window ||
  654. || may be different next time (for example, Opus has reopenned on another screen).      ||
  655. || This is a bit of a hack, but Jonathan Potter has said it should be okay.              ||
  656. `-====================================================================================*/
  657. struct Window *getListerWindow(Compare_Data *data)
  658. {
  659.     struct path_node pn;
  660.  
  661.     // Setup some semi-sensible defaults (and hope they'll do!)
  662.     pn.buffer[0] = '\0';
  663.     pn.path = pn.buffer;
  664.     pn.flags = NULL;
  665.  
  666.     pn.lister = (data->listerhandle);
  667.  
  668.     return((struct Window *)\
  669.                 data->func_callback(EXTCMD_GET_WINDOW,IPCDATA(data->ipc),(APTR)&pn));
  670. }
  671.  
  672. /*= getDOpusScreen() =================================================================-.
  673. || Attempts to get the Opus screen, returns it or NULL.                                  ||
  674. || The return should not be stored for later use as the screen may be different next  ||
  675. || time if the user has changed it.                                                      ||
  676. `-====================================================================================*/
  677. struct Screen *getDOpusScreen(Compare_Data *data)
  678. {
  679.     struct Screen *screen = NULL;
  680.     struct DOpusScreenData *dsd;
  681.  
  682.     if (dsd = (struct DOpusScreenData *)\
  683.                 data->func_callback(EXTCMD_GET_SCREENDATA,IPCDATA(data->ipc),NULL))
  684.     {
  685.         screen = dsd->screen;
  686.  
  687.         data->func_callback(EXTCMD_FREE_SCREENDATA,IPCDATA(data->ipc),(APTR)dsd);
  688.     }
  689.     return(screen);
  690. }
  691.  
  692. /*= report_full_display() ============================================================-.
  693. || Based on the linked list of DiffRanges produced by compare_files(), this routine      ||
  694. || builds a side-by-side Hex/ASCII dump of the differences between the two files.      ||
  695. ||------------------------------------------------------------------------------------||
  696. || Nasty long routine alert!                                                          ||
  697. ||------------------------------------------------------------------------------------||
  698. || Unlike the comparison routine, there is no fixed buffer size for this routine: it  ||
  699. || will attempt to allocate enough memory to hold two versions of each DiffRange.      ||
  700. || Only one DiffRange is dealt with at a time, but it's always possible that every      ||
  701. || byte is different and the files are large. This is hardly a concern as:              ||
  702. || a) If there are millions of differences it's unlikely that the user will want to      ||
  703. ||      see them all. ("Duh, It's no case of just a different version string, Bob,      ||
  704. ||      these files really are different and the filesize is coincidence!").              ||
  705. || b) The report file goes to T: and the DOpus text viewer loads it all at once, and  ||
  706. ||      the report file is several times larger than any one DiffRange in the set which ||
  707. ||      produces it, so if there isn't enough memory for a DiffRange there isn't going  ||
  708. ||      to be enough to display the final report anyway. (And I'm not about to rewrite  ||
  709. ||      the Opus text viewer or faff about with offering destinations other than T:,      ||
  710. ||      given than point (a) is pretty stand-alone anyway.)                              ||
  711. || If you're a complete psycho, feel free to think this is a bad way of doing it, be  ||
  712. || my guest and recode it. Then see if it makes the blindest bit of difference in      ||
  713. || The Real World(tm).                                                                  ||
  714. || (Anyone would think I was feeling inadequate, or something...) Ahh-hu-hu-huh-hem...||
  715. `-====================================================================================*/
  716. void report_full_display(Compare_Data *data)
  717. {
  718.     LONG errkeep;
  719.     struct DiffRange *drnew;    // New/current DiffRange structure.
  720.     ResNode *dislinern;
  721.     ResNode *tempfilern;
  722.     ResNode *hexbuf1rn;
  723.     ResNode *hexbuf2rn;
  724.     ResNode *ascbuf1rn;
  725.     ResNode *ascbuf2rn;
  726.     BPTR outfile;            // -.
  727.     char *disline;            //  |_ Copies of things from ResNodes
  728.     char *f1block;            //  |  for shorter/simpler lines of code.
  729.     char *f2block;            // -'
  730.     LONG donebytes;            // How far into the file we are. (start of current DiffRng)
  731.     LONG dobytes;            // How many bytes to do in the current DiffRange.
  732.     APTR progwin;
  733.     ULONG tsecs;            // -._ Used to get a unique filename
  734.     ULONG tmics;            // -'  for the tempfile in T:.
  735.     ResNode *combufrn;
  736.     struct command_packet cp;
  737.  
  738.     // Open progress window.
  739.     if (progwin = OpenProgressWindowTags(\
  740.                 PW_Window,        getListerWindow(data),
  741.                 PW_Title,        dgs(MSG_PROGTITLE),
  742.                 PW_FileName,    dgs(MSG_GENERATING),
  743.                 PW_FileCount,    data->minsize,
  744.                 PW_Flags,        PWF_GRAPH|PWF_ABORT|PWF_FILENAME,
  745.                 TAG_END))
  746.     {
  747.         if ( (dislinern = allocNewResNode(&data->rnd,DISPLAYLINEBUFFSIZE)) && \
  748.              (hexbuf1rn = allocNewResNode(&data->rnd,HEXBUFFSIZE)) && \
  749.              (hexbuf2rn = allocNewResNode(&data->rnd,HEXBUFFSIZE)) && \
  750.              (ascbuf1rn = allocNewResNode(&data->rnd,ASCBUFFSIZE)) && \
  751.              (ascbuf2rn = allocNewResNode(&data->rnd,ASCBUFFSIZE)) && \
  752.              (tempfilern = allocNewResNode(&data->rnd,PATHBUFFSIZE)) )
  753.         {
  754.             disline = (dislinern->rn_Mem);
  755.             donebytes = 0;
  756.  
  757.             SetProgressWindowTags(progwin,
  758.                 PW_FileNum,donebytes,
  759.                 TAG_END);
  760.  
  761.             // Generate a unique filename. Virtually impossible to get two reports
  762.             // from the same lister within one second of each other. Using the micros
  763.             // value in the filename runs the risk of generating a name over 30 chars.
  764.             CurrentTime(&tsecs,&tmics);
  765.             sprintf(tempfilern->rn_Mem,"t:cmp_%lu_%lu.tmp",data->listerhandle,tsecs);
  766.             (tempfilern->rn_Name) = (tempfilern->rn_Mem);
  767.  
  768.             // This is the line identifying which file is on which side of the output.
  769.             sprintf(disline,dgs(MSG_LEFTRIGHT),data->file1path,data->file2path);
  770.  
  771.             // Attempt to open the temp file for writting to.
  772.             outfile = openFileResNode(&data->rnd,tempfilern,MODE_NEWFILE);
  773.  
  774.             // Flag that the file should be deleted automatically.
  775.             // (This flag will be removed if the file gets to the Opus viewer
  776.             // successfully as it will be left to Opus to delete it after we
  777.             // have exited.) -- Safe to set flag even if file didn't open.
  778.             (tempfilern->rn_TempFile) = TRUE;
  779.  
  780.             // Now check the open and write the header line.
  781.             if (!( (outfile) && (0 < Write(outfile,disline,strlen(disline))) ))
  782.             {
  783.                 // If we couldn't open or write to the file, error message & abort.
  784.                 informUser(data,dgs(MSG_ERRWRITE),TRUE,NULL);
  785.                 outfile = NULL;        // Signal to abort.
  786.             }
  787.             else
  788.             {
  789.                 // Now output the differences dump for each DiffRange in turn.
  790.                 // If outfile goes NULL it will abort the loop.
  791.  
  792.                 for(drnew = (data->drbase); (outfile) && (drnew); drnew = drnew->next)
  793.                 {
  794.                     // Include DR_EXTENT bytes either side of the interval,
  795.                     // but make sure we don't try to read outside of the file.
  796.                     // (The DiffRange may already point outside the file, but we'll
  797.                     // also fix that here anyway.)
  798.  
  799.                     if (0 > ((drnew->start) -= DR_EXTEND))
  800.                         (drnew->start) = 0;
  801.                     if ((data->minsize) <= ((drnew->end)+=DR_EXTEND))
  802.                         (drnew->end) = ((data->minsize)-1);
  803.  
  804.                     // Set number of bytes done (also start offset in file).
  805.                     donebytes = (drnew->start);
  806.  
  807.                     // Calculate the interval (block) size and allocate two buffers
  808.                     // associated with each file.
  809.  
  810.                     dobytes = ( ((drnew->end)-(drnew->start)) + 1 );
  811.  
  812.                     if (!( (f1block = (char *)\
  813.                                 allocMemResNode(&data->rnd,data->file1rn,dobytes)) && \
  814.                            (f2block = (char *)\
  815.                                 allocMemResNode(&data->rnd,data->file2rn,dobytes)) ))
  816.                     {
  817.                         // Free the file1 memory if it got allocated.
  818.                         freeMemResNode(&data->rnd,data->file1rn);
  819.  
  820.                         // Close & delete output file to free as much mem as we can.
  821.                         deleteResNode(&data->rnd,tempfilern);
  822.                         tempfilern = NULL;    // Make safe the delete attempt below.
  823.                         outfile = NULL;        // Signal to abort.
  824.                         informUser(data,dgs(MSG_OUTOFMEM),TRUE,NULL);
  825.                     }
  826.                     else
  827.                     {
  828.                         Seek(data->file1rn->rn_FHandle,drnew->start,OFFSET_BEGINNING);
  829.                         errkeep = IoErr();
  830.                         Seek(data->file2rn->rn_FHandle,drnew->start,OFFSET_BEGINNING);
  831.                         errkeep = (errkeep | IoErr());
  832.  
  833.                         if (errkeep)
  834.                         {
  835.                             // Free the two buffers to get as much mem as we can.
  836.                             freeMemResNode(&data->rnd,data->file1rn);
  837.                             freeMemResNode(&data->rnd,data->file2rn);
  838.  
  839.                             // Close & delete temp file to free as much mem as we can.
  840.                             deleteResNode(&data->rnd,tempfilern);
  841.                             tempfilern = NULL;    // Make safe the delete attempt below.
  842.                             outfile = NULL;        // Signal to abort.
  843.                             informUser(data,dgs(MSG_READFAIL),TRUE,NULL);
  844.                         }
  845.                         else
  846.                         {
  847.                             // Attempt to read the files into the buffers.
  848.                             if ((0 >= Read(data->file1rn->rn_FHandle,f1block,dobytes))\
  849.                              || (0 >= Read(data->file2rn->rn_FHandle,f2block,dobytes)))
  850.                             {
  851.                                 // Free the two buffers to get as much mem as we can.
  852.                                 freeMemResNode(&data->rnd,data->file1rn);
  853.                                 freeMemResNode(&data->rnd,data->file2rn);
  854.  
  855.                                 // Close & delete temp file to free as much mem as can.
  856.                                 deleteResNode(&data->rnd,tempfilern);
  857.                                 tempfilern = NULL;    // Make safe the delete below.
  858.                                 outfile = NULL;        // Signal to abort.
  859.                                 informUser(data,dgs(MSG_READFAIL),TRUE,NULL);
  860.                             }
  861.                             else
  862.                             {
  863.                                 if (!(outrepline(data,outfile,f1block,f2block,dobytes,
  864.                                                  donebytes,disline,
  865.                                                  hexbuf1rn->rn_Mem,hexbuf2rn->rn_Mem,
  866.                                                  ascbuf1rn->rn_Mem,ascbuf2rn->rn_Mem)))
  867.                                 {
  868.                                     // Free the two buffers to get as much mem as can.
  869.                                     freeMemResNode(&data->rnd,data->file1rn);
  870.                                     freeMemResNode(&data->rnd,data->file2rn);
  871.  
  872.                                     // Close & delete temp file to free as much mem.
  873.                                     deleteResNode(&data->rnd,tempfilern);
  874.                                     tempfilern = NULL;    // Make safe the delete below.
  875.                                     outfile = NULL;        // Signal to abort.
  876.                                     informUser(data,dgs(MSG_ERRWRITE),TRUE,NULL);
  877.                                 }
  878.                                 else
  879.                                 {
  880.                                     SetProgressWindowTags(progwin,
  881.                                         PW_FileNum,donebytes,
  882.                                         TAG_END);
  883.                                 }
  884.                             }
  885.                         }
  886.                         // Free the two buffers ready for the next interval (block).
  887.                         // Safe to call if mem already freed.
  888.                         freeMemResNode(&data->rnd,data->file1rn);
  889.                         freeMemResNode(&data->rnd,data->file2rn);
  890.                     }
  891.                 }
  892.  
  893.                 // If the file is still open there wasn't an error.
  894.                 if (outfile)
  895.                 {
  896.                     // Make sure the progress window gets to 100%
  897.                     SetProgressWindowTags(progwin,
  898.                         PW_FileNum,data->minsize,
  899.                         TAG_END);
  900.  
  901.                     // Stop the file being deleted automatically when the ResNode is.
  902.                     // We're going to run "dopus read delete <filename>"
  903.                     // asynchronously and let Opus delete the file when it's finished.
  904.                     (tempfilern->rn_TempFile) = FALSE;
  905.  
  906.                     // We cannot delete the tempfilern yet as it still contains the
  907.                     // filename. We have to close the file, though.
  908.  
  909.                     closeFileResNode(&data->rnd,tempfilern);
  910.                     outfile = NULL;
  911.  
  912.                     // Show it to the user.
  913.                     if (combufrn = allocNewResNode(&data->rnd,COMMBUFFSIZE))
  914.                     {
  915.                         sprintf(combufrn->rn_Mem,"dopus read delete %s",
  916.                                 tempfilern->rn_Name);
  917.                         sendExtCmd_nr(data,combufrn->rn_Mem,&cp);
  918.  
  919.                         deleteResNode(&data->rnd,combufrn);
  920.                         combufrn = NULL;
  921.                     }
  922.                 }
  923.             }
  924.             // These ResNode pointers must be valid or NULL -- if they get
  925.             // deleted in some error handling code above the code must also NULL
  926.             // the relevante ResNode pointer as well.
  927.             deleteResNode(&data->rnd,tempfilern);
  928.             deleteResNode(&data->rnd,dislinern);
  929.             deleteResNode(&data->rnd,hexbuf1rn);
  930.             deleteResNode(&data->rnd,hexbuf2rn);
  931.             deleteResNode(&data->rnd,ascbuf1rn);
  932.             deleteResNode(&data->rnd,ascbuf2rn);
  933.         }
  934.         CloseProgressWindow(progwin);
  935.     }
  936. }
  937.  
  938. /*= outrepline() =====================================================================-.
  939. || Writes the actual side-by-side hex dump to the output file of the full display.      ||
  940. || Returns boolean success, Doesn't attempt to clean anything up on failure, that's      ||
  941. || left to the calling routine to keep things simpler.                                  ||
  942. || Doesn't send error messages, caller should free buffers and report the error.      ||
  943. `-====================================================================================*/
  944. BOOL outrepline(Compare_Data *data,BPTR outfile,char *f1block,char *f2block,\
  945.                 LONG dobytes, LONG donebytes,char *disline,char *hex1,char *hex2,
  946.                 char *asc1,char *asc2)
  947. {
  948.     BOOL orl_return = TRUE;
  949.     char *disp;
  950.     char *h1;
  951.     char *h2;
  952.     char *a1;
  953.     char *a2;
  954.     BOOL indif;        // When true, the inverse-ANSI code is 'active'.
  955.     int i,j;
  956.  
  957.     while( (orl_return) && (dobytes > 0) )
  958.     {
  959.  
  960. /*- Generate the various components in hex1,hex2,asc1,asc2... ------------------------*/
  961.  
  962.         h1 = hex1;        // -.
  963.         h2 = hex2;        //  |_ We use these new pointers while building the string.
  964.         a1 = asc1;        //  |  Need to keep the old pointers to the begginings.
  965.         a2 = asc2;        // -'
  966.  
  967.         indif = FALSE;
  968.  
  969.         for (j = 0; j < 2; j++)
  970.         {
  971.             for (i = 0; i < 4; i++)
  972.             {
  973.                 if (dobytes > 0)
  974.                 {
  975.                     if ( (*f1block) == (*f2block) )
  976.                     {
  977.                         if (indif)
  978.                         {
  979.                             h1 = stpcpy(h1,"");
  980.                             h2 = stpcpy(h2,"");
  981.                             a1 = stpcpy(a1,"");
  982.                             a2 = stpcpy(a2,"");
  983.                             indif = FALSE;
  984.                         }
  985.                         sprintf(h1,"%02x",(int)(*f1block));
  986.                         h2 = stpcpy(h2,h1);
  987.                         h1 += 2;
  988.                         (*(a1++)) = (*(a2++)) = printchar(*f1block);
  989.                     }
  990.                     else
  991.                     {
  992.                         if (!(indif))
  993.                         {
  994.                             h1 = stpcpy(h1,"");
  995.                             h2 = stpcpy(h2,"");
  996.                             a1 = stpcpy(a1,"");
  997.                             a2 = stpcpy(a2,"");
  998.                             indif = TRUE;
  999.                         }
  1000.                         sprintf(h1,"%02x",(int)(*f1block));
  1001.                         h1 += 2;
  1002.                         sprintf(h2,"%02x",(int)(*f2block));
  1003.                         h2 += 2;
  1004.                         (*(a1++)) = printchar(*f1block);
  1005.                         (*(a2++)) = printchar(*f2block);
  1006.                     }
  1007.  
  1008.                     f1block++;
  1009.                     f2block++;
  1010.                     dobytes--;
  1011.                 }
  1012.                 else
  1013.                 {
  1014.                     if (indif)
  1015.                     {
  1016.                         h1 = stpcpy(h1,"");
  1017.                         h2 = stpcpy(h2,"");
  1018.                         a1 = stpcpy(a1,"");
  1019.                         a2 = stpcpy(a2,"");
  1020.                         indif = FALSE;
  1021.                     }
  1022.                     (*(h1++)) = (*(h2++)) = (*(a1++)) = (*(a2++)) = ' ';
  1023.                     (*(h1++)) = (*(h2++)) = ' ';
  1024.                 }
  1025.             }
  1026.  
  1027.             // Make sure the ANSI codes are reset at the end of the strings.
  1028.             if (indif)
  1029.             {
  1030.                 h1 = stpcpy(h1,"");
  1031.                 h2 = stpcpy(h2,"");
  1032.                 a1 = stpcpy(a1,"");
  1033.                 a2 = stpcpy(a2,"");
  1034.                 indif = FALSE;
  1035.             }
  1036.  
  1037.             // Always add a space after the hex strings.
  1038.             // If we've just done the second four (out of eight), NULL terminate all
  1039.             // strings.
  1040.  
  1041.             (*(h1++)) = (*(h2++)) = ' ';
  1042.             if (i > 0)
  1043.             {
  1044.                 (*h1) = (*h2) = (*a1) = (*a2) = '\0';
  1045.             }
  1046.         }
  1047.  
  1048. /*- Join the various pieces together into one line... --------------------------------*/
  1049.  
  1050.         // Offset.
  1051.         sprintf(disline,"%08lx | ",donebytes);
  1052.         disp = (disline + 11);    // Point to just after "89ABCDEF: "
  1053.  
  1054.         donebytes += 8;
  1055.  
  1056.         disp = stpcpy(disp,hex1);            // Left hex dump.
  1057.         disp = stpcpy(disp,asc1);            // Left ASCII dump.
  1058.         disp = stpcpy(disp," | ");            // Divider.
  1059.         disp = stpcpy(disp,hex2);            // Right hex dump.
  1060.         disp = stpcpy(disp,asc2);            // Right ASCII dump.
  1061.                stpcpy(disp,"\n");            // End of line.
  1062.  
  1063.         // Write to the output.
  1064.         if (0 >= Write(outfile,disline,strlen(disline)))
  1065.         {
  1066.             orl_return = FALSE;        // Flag failure (Abort).
  1067.         }
  1068.     }
  1069.  
  1070.     if (orl_return)
  1071.     {
  1072.         if (0 >= Write(outfile,"\n",strlen("\n")))
  1073.         {
  1074.             orl_return = FALSE;        // Flag failure (Abort).
  1075.         }
  1076.     }
  1077.  
  1078.     return(orl_return);
  1079. }
  1080.  
  1081. /*= sendExtCmd_nr() ==================================================================-.
  1082. || Function to make calling Opus ARexx commands slightly cleaner. This version for      ||
  1083. || when you do not want a result string.                                              ||
  1084. `-====================================================================================*/
  1085. void sendExtCmd_nr(Compare_Data *data,char *cmdstring,struct command_packet *cpp)
  1086. {
  1087.     // We do NOT want a result, but dopus5.library seems prone to
  1088.     // memory leaks when one isn't requested for certain commands,
  1089.     // so we'll ask for one and free it immediately afterwards.
  1090.     cpp->flags = COMMANDF_RESULT;
  1091.     cpp->command = cmdstring;
  1092.     data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),cpp);
  1093.     FreeVec(cpp->result);
  1094.     cpp->result = NULL;
  1095. }
  1096.  
  1097. /*= copyword() =======================================================================-.
  1098. || Copy the string from source to dest until the first space or NULL in source.          ||
  1099. `-====================================================================================*/
  1100. void copyword(char *dest,char *source)
  1101. {
  1102.     while((*source) && (*source != ' '))
  1103.         *(dest++) = *(source++);
  1104.  
  1105.     *dest = '\0';
  1106. }
  1107.  
  1108. /*= copywordquoted() =================================================================-.
  1109. || Copy the string from source to dest until the first quote or NULL in source.          ||
  1110. || If the source string starts with a quote it will be skipped.                          ||
  1111. `-====================================================================================*/
  1112. void copywordquoted(char *dest,char *source)
  1113. {
  1114.     if (*source == '"')
  1115.         source++;
  1116.  
  1117.     while((*source) && (*source != '"'))
  1118.         *(dest++) = *(source++);
  1119.  
  1120.     *dest = '\0';
  1121. }
  1122.  
  1123. /*= parseArgs() ======================================================================-.
  1124. || Parses command-line arguments and sets the "BUFFSIZE" number.                      ||
  1125. || If there is no command-line, or some kind of error/problem occurs during parsing,  ||
  1126. || defaults will be used.                                                              ||
  1127. `-====================================================================================*/
  1128. FuncArgs *parseArgs(Compare_Data *data,char *args)
  1129. {
  1130.     FuncArgs *fa;
  1131.  
  1132.     // Defaults.
  1133.     (data->buffsize) = DEFCOMPAREBUFFSIZE;
  1134.     (data->nosizewarn) = FALSE;
  1135.  
  1136.     if (fa = ParseArgs(CMD_TEMPLATE,args))
  1137.     {
  1138.         (data->nosizewarn) = (BOOL)((fa->FA_Arguments)[ARG_NOSIZEWARN]);
  1139.  
  1140.         if ( ((fa->FA_Arguments)[ARG_BUFFSIZE]) && 
  1141.              (0 < (*(LONG *)((fa->FA_Arguments)[ARG_BUFFSIZE]))) )
  1142.             (data->buffsize) = *(LONG *)((fa->FA_Arguments)[ARG_BUFFSIZE]);
  1143.     }
  1144.  
  1145.     return(fa);
  1146. }
  1147.  
  1148. /*= freeArgs() =======================================================================-.
  1149. || Frees the structure returned by parseArgs(), if one returned at all.                  ||
  1150. || All pointers into the structure will be invalid after this call.                      ||
  1151. `-====================================================================================*/
  1152. void freeArgs(FuncArgs *fa)
  1153. {
  1154.     if (fa)
  1155.         DisposeArgs(fa);
  1156. }
  1157.